package com.atguigu.spzx.manager.service.impl;

import cn.hutool.captcha.CaptchaUtil;
import cn.hutool.captcha.CircleCaptcha;
import cn.hutool.core.util.IdUtil;
import com.alibaba.fastjson.JSON;
import com.atguigu.spzx.common.exception.SpzxException;
import com.atguigu.spzx.common.interceptor.SpzxLoginAuthInterceptor;
import com.atguigu.spzx.manager.mapper.SysUserMapper;
import com.atguigu.spzx.manager.service.SysUserService;
import com.atguigu.spzx.model.dto.system.LoginDto;
import com.atguigu.spzx.model.dto.system.SysUserDto;
import com.atguigu.spzx.model.entity.system.SysUser;
import com.atguigu.spzx.model.vo.common.ResultCodeEnum;
import com.atguigu.spzx.model.vo.system.LoginVo;
import com.atguigu.spzx.model.vo.system.ValidateCodeVo;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import jakarta.annotation.Resource;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.DigestUtils;
import org.springframework.util.StringUtils;

import java.util.Date;
import java.util.List;
import java.util.concurrent.TimeUnit;

@Service
public class SysUserServiceImpl implements SysUserService {
    @Resource
    SysUserMapper sysUserMapper;
    @Resource
    StringRedisTemplate stringRedisTemplate;
    @Override
    public LoginVo login(LoginDto loginDto) {
        //1、参数校验: controller通过对象接收前端的参数时，对象不可能为空因为springmvc会创建该对象 但是属性值可能为空
        String userName = loginDto.getUserName();
        String password = loginDto.getPassword();
        String code = loginDto.getCode();
        String codeKey = loginDto.getCodeKey();
        if(StringUtils.isEmpty(userName) ||
                StringUtils.isEmpty(password)||
                StringUtils.isEmpty(code)||
                StringUtils.isEmpty(codeKey))   {
//            throw new RuntimeException("参数校验失败");
            throw new SpzxException(ResultCodeEnum.PARAMS_ERROR,null);
        }
        //验证码校验
        String cacheCode = stringRedisTemplate.opsForValue().get("user:login:code:" + codeKey);
        if(!code.equals(cacheCode)){
            throw new SpzxException(ResultCodeEnum.CODE_ERROR,null);
        }
        stringRedisTemplate.delete("user:login:code:" + codeKey);
        //2、根据账号查询用户信息
        SysUser sysUser = sysUserMapper.findSysUserByUserName(userName);
        if(sysUser==null){
//            throw new RuntimeException("用户不存在");
            throw new SpzxException(ResultCodeEnum.LOGIN_ERROR,null);
        }
        //3、判断密码是否正确
        //MD5是不可逆加密：不能根据密文还原原数据  必须对登录的明文加密和数据库的密文比较
        String inputEncodePws = DigestUtils.md5DigestAsHex(password.getBytes());
        if(!inputEncodePws.equals(sysUser.getPassword())){
            throw new SpzxException(ResultCodeEnum.LOGIN_ERROR,null);
        }
        //4、判断用户状态是否可用
        if(sysUser.getStatus()!=1){
//            throw new RuntimeException("账户停用");
            throw new SpzxException(ResultCodeEnum.ACCOUNT_ERROR,null);
        }
        //登录成功
        //保持登录状态：存到redis中
        String token = IdUtil.getSnowflake(1,1).nextIdStr();
        //token + sysUser 保存到redis中:  sysUser密码是加密过的，如果是未加密的或者简单加密 可以数据脱敏然后存入到redis中
        stringRedisTemplate.opsForValue().set("user:login:"+token,
                // 7天
                JSON.toJSONString(sysUser), 7 , TimeUnit.DAYS );


        LoginVo loginVo = new LoginVo();
        loginVo.setToken(token);
        loginVo.setRefresh_token("1");
        return loginVo;
    }

    @Override
    public ValidateCodeVo generateValidateCode() {
        CircleCaptcha captcha = CaptchaUtil.createCircleCaptcha(90, 40, 4, 15);
        //验证码字符串
        String code = captcha.getCode();
        //Base64: 为了避免字节出现负数 导致出现乱码或特殊符号，base64可以对字节数组进行处理 转为可读的字符 转换后的字符一共有
        // 64种可能(a~z  A~Z  0~9  + /  = 作为保留字符 只用来占位)  转换后比原来的数据要大
        String imgBase64 = captcha.getImageBase64();
        String idStr = IdUtil.getSnowflake(1, 1).nextIdStr();
        //缓存验证码到redis中  过期时间为15分钟
        stringRedisTemplate.opsForValue().set("user:login:code:"+idStr , code , 15 , TimeUnit.MINUTES);

        ValidateCodeVo codeVo = new ValidateCodeVo();
        codeVo.setCodeImg(imgBase64);
        codeVo.setCodeKey(idStr);
        return codeVo;
    }

    @Override
    public SysUser getUserinfo() {
        //获取透传过来的sysUser对象
//        SysUser user = SpzxLoginAuthInterceptor.CACHE.get(Thread.currentThread());
//        System.out.println(user);
        SysUser sysUser = SpzxLoginAuthInterceptor.THREAD_LOCAL.get();
//        System.out.println(user);
//        Thread.currentThread() 获取调用当前方法的线程对象
        sysUser.setPassword(null);
        return sysUser;
    }

    @Override
    public void logout(String token) {
        stringRedisTemplate.delete("user:login:" + token);
    }

    @Override
    public PageInfo<SysUser> findPage(Integer pageNum, Integer pageSize, SysUserDto sysUserDto) {
        //参数3：是否统计数量   参数4：开启分页合理化 避免页码超出范围查询的数据为空
        PageHelper.startPage(pageNum , pageSize , true , true , null);
        List<SysUser> sysUserList = sysUserMapper.findPage(sysUserDto);
        return new PageInfo<SysUser>(sysUserList);
    }

    @Override
    public void deleteById(Long id) {
        sysUserMapper.deleteById(id);
    }

    @Override
    public void save(SysUser sysUser) {
        sysUser.setCreateTime(new Date());
        sysUser.setUpdateTime(new Date());
        //密码需要加密: 加密方式必须和 登录时验证密码的加密方式一样
        sysUser.setPassword(DigestUtils.md5DigestAsHex(sysUser.getPassword().getBytes()));
        sysUserMapper.save(sysUser);
    }

    @Override
    public SysUser getById(Long id) {
        return sysUserMapper.getById(id);
    }

    @Override
    public void update(SysUser sysUser) {
        sysUser.setUpdateTime(new Date());
        sysUserMapper.updateById(sysUser);
    }

    @Override
    public void batchDel(List<Long> ids) {
        if(CollectionUtils.isEmpty(ids)){
            return;
        }
        sysUserMapper.batchDel(ids);
    }
}
